#include "dictionary.h"

#include "vfs/vfs.h"

#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    
#include "util/file_io.h"		    
#include <string.h>
#include <cassert>
#include <ctype.h>

using namespace lang;
using namespace util;

Dictionary::Dictionary() :
	m_RecordC(0),
	m_RecordA(0)
{

}

Dictionary::~Dictionary()
{
	UTIL_SAFE_DELETEA(m_RecordA);
}

bool	Dictionary::LoadFromFile(const std::string& File)
{
	vfs::LoadedBlock		Block;
	if (!vfs::VFS::Instance().GetFile(File, Block))
	{
		_LOG(MSG_ERROR, "Failed to load dictionary " << File);
		return false;
	}

	DictHeader		Header;
	Block.ReadData(&Header, sizeof(Header));

	if (Header.MagicNum != Header.GetMagicNum() ||
		Header.VersionNum != Header.GetVersionNum())
	{
		_LOG(MSG_ERROR, "Invalid header loading Dictionary " << File);
		return false;
	}

	// parse header
	m_RecordC = Header.WordC;
	m_RecordA = new Record[m_RecordC];
	memcpy(m_LetterIndex, Header.LetterIndex, sizeof(m_LetterIndex));

	// load data
	Block.ReadData(m_RecordA, sizeof(Record)*m_RecordC);

	// close file


/*	IsValidWord("Fooo");
	IsValidWord("asdaslkd;aksd;asldka;sldk");
	IsValidWord("_23");
	IsValidWord("hello");*/
	return true;
}

bool	Dictionary::GetWordByNumber(int Index, std::string& Word)
{
	if (Index >= 0 && Index < (int)m_RecordC)
	{
		Word = m_RecordA[Index].Data;
	}
	return true;
}

bool	Dictionary::IsValidWord(const std::string& Word)
{
	if (Word.empty())
		return false;		// empty string

	if (Word.size() >= MAX_WORD_SIZE)
		return false;		// too big word.. buffer overflow

	char		Buffer[MAX_WORD_SIZE];
	
	
	strcpy(Buffer, Word.c_str());
#ifdef WIN32
	strupr(Buffer);
#else 
	//CHIT : TODO : is it really correct?
	//__gnu_cxx::transform(Buffer, &Buffer[Word.size()], Buffer,
	//			   (int(*)(int)) std::toupper);
#endif
	
	
	unsigned char	FirstLetter = Buffer[0];

	if (FirstLetter < 'A' || FirstLetter > 'Z') 	return false;	// outside range
	int	StartIndex = m_LetterIndex[FirstLetter - 'A'];
	int	EndIndex = m_RecordC;
	if (StartIndex != 'Z')
		EndIndex = m_LetterIndex[FirstLetter - 'A' + 1];

	int FoundLen = 0;
	int	Length = (int)strlen(Buffer);

	for (int i=StartIndex; i<EndIndex; i++)
	{
		int	CurrentWordLen = (int)strlen(m_RecordA[i].Data);
		if (CurrentWordLen!= Length)
			continue;

		int FoundLenNow = 0;
		
		for (int j = 0; j<Length; j++)
		{
	//	support wildcards?
			if (Buffer[j] == m_RecordA[i].Data[j] )
				FoundLenNow++;
		}
		if (FoundLenNow == Length)
			return true;
		if (FoundLenNow < FoundLen)
			return false;
		if (FoundLenNow > FoundLen)
			FoundLen = FoundLenNow;
	}
	return false;
}

bool	Dictionary::AddWord(const std::string& Word)
{
	return true;
}

bool	Dictionary::RemoveWord(const std::string& Word)
{
	return true;
}

int		Dictionary::GetWordCount() const
{
	return m_RecordC;
}



typedef struct FPSDICHEADERTAG
{
	char szBeginCode[20];
	char szAuthor[50];
	char szDateCreated[12];
	char szDateUpdated[12];
	char szLanguage[20];
	unsigned int uiCompress;
	char szFiller[250];
	char szEndCode[20];
} FPSDICHEADER;

// this struct defines the records used in dictionary
// files
#define FPSSPELLCHECK_MAXWORDLEN		18
#define FPSSPELLCHECK_MAXMETALEN		8
#define FPSSPELLCHECK_MAXREDUCEDLEN		8

typedef struct FPSDICWORDTAG
{
	char szWord[FPSSPELLCHECK_MAXWORDLEN+1];
	char szMetaphone[FPSSPELLCHECK_MAXMETALEN+1];
	char szReduced[FPSSPELLCHECK_MAXREDUCEDLEN+1];
	bool bCaseSensitive;
	int iWordLen;
} FPSDICWORD;

bool	Dictionary::ConvertFromObsolete(const std::string& Input, const std::string& Output)
{
	FileIO	FileOutput;
	vfs::LoadedBlock		Block;
	if (!vfs::VFS::Instance().GetFile(Input, Block))
	{
		_LOG(MSG_ERROR, "Failed to load input" << Input);
		return false;
	}

	// work out size
	
	int				Len = Block.Size;

	int				Records = (Len - sizeof(FPSDICHEADER)) / sizeof(FPSDICWORD);
	FPSDICWORD*		InWords = new FPSDICWORD[Records+1];
	FPSDICHEADER	InFileHeader;
	int				IndexRecords[255];
	memset(&InFileHeader, 0, sizeof(FPSDICHEADER));
	memset(&IndexRecords, 0, sizeof(IndexRecords));

	Block.ReadData(&InFileHeader, sizeof(FPSDICHEADER));
	Block.ReadData(InWords, Len);

// export
	FileOutput.Open(Output, FileIO::FILE_BINARY|FileIO::FILE_WRITE);

	DictHeader		Header;
	Header.MagicNum		= Header.GetMagicNum();
	Header.VersionNum	= Header.GetVersionNum();
	memset(Header.LetterIndex, 0, sizeof(Header.LetterIndex));

	Record*	ConvertedWords = new Record[Records];

	unsigned int		ValidWordC	 = 0;
	unsigned int		CurrentLetter = 'A';
	Header.LetterIndex[CurrentLetter - 'A'] = 0;

	for (int i=0; i<Records; i++)
	{
		if ((toupper(InWords[i].szWord[0]) <= 'Z') && 
			 (toupper(InWords[i].szWord[0]) >= 'A'))
		{
			strcpy(ConvertedWords[ValidWordC].Data, InWords[i].szWord);
#ifdef WIN32
			strupr(ConvertedWords[ValidWordC].Data);
#else
			//__gnu_cxx::transform(ConvertedWords[ValidWordC].Data, &ConvertedWords[ValidWordC].Data[FPSSPELLCHECK_MAXWORDLEN], ConvertedWords[ValidWordC].Data,
			//			   (int(*)(int)) std::toupper);
#endif
			
			
			if (ConvertedWords[ValidWordC].Data[0] != CurrentLetter)
			{
				CurrentLetter = ConvertedWords[ValidWordC].Data[0];
				Header.LetterIndex[CurrentLetter - 'A'] = ValidWordC;
			}
			ValidWordC++;
		}
	}
	Header.WordC		= ValidWordC;	// some have been excluded for being outside a..z
	FileOutput.Write(&Header, sizeof(Header));
	FileOutput.Write(ConvertedWords, sizeof(Record)*Header.WordC);
	delete [] ConvertedWords;
	delete [] InWords;
	return true;
}
